'''
Knowtemarks
===========
A simple tool to manage your notes and bookmarks

@author: Markus Fuchs, 2012
'''
from tornado.web import RequestHandler
from knowtemarks.DataModel import Knowte, Tag
from knowtemarks.search.Filter import SearchStringFilter, TypeFilter, TagFilter,\
    SearchFilter
from knowtemarks.search import getResults
from knowtemarks.tools.Import import DeliciousHTMLImporter, MozillaJSONImporter
import datetime, traceback

class Model(object):
    """
    This class conains the data to be displayed in all the
    templates. It defines some default variables which are assumed
    to be present in this application.
    """
    def __init__(self):
        self.errors=[]
        self.bug = False
        self.traceback = None
        self.status = 200
        self.successMsg = ""
        self.alertMsg = ""
        self.errorMsg = ""
        self.item = Knowte(url="http://")
        self.view = "index.tpl"
        self.rendered = False # never touch this
    def getArgs(self):
        return self.__dict__


class BaseHandler(RequestHandler):
    """
    A request handler which all our handlers should extend.
    It provides objects to access the database.
    
    Basic Usage:
    All inheriting classes should override doGet() and doPost()
    instead of get() and post(). They set the name of the view by
        self.model.view = "viewname"
    which is automatically rendered after the doGet/doPost methods
    return. If you want to redirect to a URL set
        self.model.view = "redirect:SOMEURL"
    
    This base class also provides some error handling. It catches 
    exceptions and renders a seperate page containing the traceback.
    """
    def __init__(self, application, request, knowteDao, **kwargs):
        RequestHandler.__init__(self, application, request, **kwargs)
        self.knowteDao = knowteDao
        self.model = Model()
        self.model.noteCount = self.knowteDao.getNoteCount()
        self.model.bookmarkCount = self.knowteDao.getBookmarkCount()
    def doGet(self, *args, **kwargs):
        pass
    def get(self, *args, **kwargs):
        self.doGet(*args, **kwargs)
        if self.model.rendered: return
        if(self.model.view.startswith("redirect:")):
            self.redirect(self.model.view.split(":")[1])
        else:
            self.render(self.model.view, **self.model.__dict__)
    def doPost(self, *args, **kwargs):
        pass
    def post(self, *args, **kwargs):
        self.doPost(*args, **kwargs)
        if self.model.rendered: return
        if(self.model.view.startswith("redirect:")):
            self.redirect(self.model.view.split(":")[1])
        else:
            self.render(self.model.view, **self.model.__dict__)
    def write_error(self, status_code, **kwargs):
        if kwargs.has_key("exc_info"):
            self.model.errors.append(kwargs["exc_info"][1])
            self.model.bug = True
            self.model.traceback = traceback.format_exc().replace("\n", "<br>").replace(" ", "&nbsp;")

        self.model.status = status_code
        self.model.rendered = True # prevent sending headers again in get/post()
        self.render("error.tpl", **self.model.__dict__)



##############################################################
# 
# The following handlers actually do something ;)
#
class NotFoundHandler(BaseHandler):
    """
    A handler which renders a 404 page.
    """
    def doGet(self, *args, **kwargs):
        self.model.errors.append("The requested Resource is not available")
        self.write_error(404)


class MainHandler(BaseHandler):
    """
    The "root" page with the newest notes and bookmarks
    """
    def doGet(self):
        self.model.newestNotes = self.knowteDao.getKnowtes(limit=5, type="note")
        self.model.newestBookmarks = self.knowteDao.getKnowtes(limit=5, type="bookmark")

        
class AddHandler(BaseHandler):
    def doGet(self):
        """ serve the add view """
        self.model.view = "add.tpl"
    
    def doPost(self):
        """ add note or bookmark to database """
        title = self.get_argument("title")
        info = self.get_argument("info", "")
        tags = self.get_argument("tags")
        url = self.get_argument("url", "")
        
        if title == "":
            # add error message
            pass
        # test tag agains regex
        # test url against regex
         
        self.model.item = Knowte(title, info, url)
        for t in tags.split(Tag.tag_seperator):
            self.model.item.addTag(t.strip())
        
        self.knowteDao.storeKnowte(self.model.item)
        self.model.view = "redirect:/"


class DeleteHandler(BaseHandler):
    def doGet(self, *args, **kwargs):
        """ print confirmation page """
        i = int(list(args)[0])
        print "Getting knowte with id %d" % i
        self.model.item = self.knowteDao.getKnowteById(i)        
        self.model.view = "delete.tpl"
    
    def doPost(self, *args, **kwargs):
        """ actually delete the item """
        k = self.knowteDao.getKnowteById(int(list(args)[0]))
        # TODO handle non-existant
        self.knowteDao.deleteKnowte(k.id)
        self.model.view = "redirect:/"

        
class SearchHandler(BaseHandler):
    """
    Until now, a quite simple search is applied:
       a. match items with a tag which is exactly the
          same as the search query
       b. match items which contain the query string 
          in their description, title or url
    
    returns items matching a as well as those matching b
    """
    def doPost(self):
        """ GET/POST doesn't matter here """
        self.get()
    def doGet(self):
        searchString = self.get_argument("q", "")
        if searchString == "":
            self.model.view = "search.tpl"
            return
        self.model.filter = SearchStringFilter(searchString, kDao=self.knowteDao)
        self.model.items = self.model.filter.filterAll()
        if self.model.items is None: self.model.items = []
        self.model.filter = [self.model.filter]
        self.model.view = "items.tpl"
        
class BrowseHandler(BaseHandler):
    """
    Browse items by filtering depending on the URL:
     /bm        show only bookmarks
     /notes     show only notes
     /t/TAGNAME show items with tag TAGNAME
    
    The following combinations are also possible:
     /bm/t/TAGNAME, /notes/t/TAGNAME
    
    They return items which match both of the filters a AND b,
    i.e. bookmarks having tag TAGNAME or notes having tag TAGNAME
    respectively
    """
    def doGet(self, *args, **kwargs):
        filters = set([SearchFilter(self.knowteDao)])
        a = list(args)
        print "DEBUG: args: " + str(a)
        if a[0] == "/bm":
            filters.add(TypeFilter("bookmark", self.knowteDao))
        elif a[0] == "/notes":
            filters.add(TypeFilter("note", self.knowteDao))
        if a[2] != None:
            filters.add(TagFilter(str(a[2]), self.knowteDao))
        filters = sorted(filters, key=lambda s: s._sortkey)

        self.model.filter = list(filters)
        self.model.items = getResults(filters, self.knowteDao) 
        self.model.view = "items.tpl"

class ImportHandler(BaseHandler):
    """
    Import items from file being uploaded.
    
    The following formats are supported by now:
      - Delicious HTML export (essentially Netscape style
        bookmarks export file)
      - Mozilla Firefox json export file
    
    This is hardly tested (works for me ;) )
    
    The parser to be used is chosen by file extension (.html, .json)
    results are immediately added to the database with a special
    tag named "import-STRFTIME" where STRFTIME formatted date/time
    (%Y%m%d%H%m%s). After import the user is redirected to the browse-view 
    for this particular tag.
    """
    def doGet(self, *args, **kwargs):
        self.model.view = "import.tpl"
    def doPost(self, *args, **kwargs):
        fileData = unicode(self.request.files['fileData'][0]["body"],"utf-8")
        fileName = self.request.files['fileData'][0]["filename"]
        
        if fileName.endswith(".json"):
            importer = MozillaJSONImporter()
            importer.setData(fileData)
        elif fileName.endswith(".htm") or fileName.endswith(".html"):
            importer = DeliciousHTMLImporter()
            importer.setData(fileData)
        dt = "import-%s" % datetime.datetime.now().strftime("%Y%m%d%H%m%s")
        self.model.items = []
        i = 0
        kl = importer.parse()
        for k in kl:
            i += 1
            k.addTag(dt)
        self.knowteDao.storeKnowtes(kl)
        self.model.successMsg = "Imported %d items with tag %s" % (i, dt)
        self.model.view = "redirect:/t/%s" % dt